package com.cwg.dict.service.impl;

import java.io.File;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.ListUtils;
import org.apache.commons.collections.Predicate;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.cwg.dict.model.History;
import com.cwg.dict.service.Historical;

public class HistoricalImpl implements Historical {

	private List<History>	histories	= new ArrayList<History>();
	private File			historyFile;
	private Logger			logger		= LoggerFactory.getLogger(HistoricalImpl.class);

	public void add(String word) {
		Date date = new Date();
		add(word, date);
	}

	public void clear() {
		histories.clear();
	}

	public File getHistoryFile() {
		return historyFile;
	}

	public boolean isFavorite(String word) {
		for (History each : histories) {
			if (StringUtils.equalsIgnoreCase(word, each.getWord())) {
				if (each.isFavorite()) {
					return true;
				}
			}
		}
		return false;
	}

	@Override
	public List<History> listBefore(final int dayAgo) {

		List<History> results = (List<History>) CollectionUtils.select(histories, new Predicate() {

			@Override
			public boolean evaluate(Object object) {
				if (!(object instanceof History)) {
					return false;
				}
				History history = (History) object;

				assert history.getDate() != null : "date field of History is required";

				// the computing is too fast to test, need delay  a while to ensure time is pass.
				int delay = 100;
				Date justNow = DateUtils.addMilliseconds(new Date(), delay);
				Date base = DateUtils.addDays(justNow, -1 + dayAgo);

				logger.debug("history date [{}] , base date [{}]", history.getDate(), base);
				return history.getDate().before(base);
			}
		});
		return ListUtils.unmodifiableList(results);
	}

	@Override
	public List<History> listLatest(int top) {
		if (top != 0 || top > histories.size()) {
			return histories.subList(0, top);
		} else {
			return histories;
		}
	}

	public void load() {
		// avoid double loaded
		clear();
		try {
			List<String> lines = FileUtils.readLines(historyFile);
			for (String line : lines) {
				logger.debug("split history entity : " + line);
				String[] splits = StringUtils.split(line, ",");
				String word = StringUtils.trim(splits[1]);
				String dateString = StringUtils.trim(splits[0]);
				boolean isFavorite = BooleanUtils.toBoolean(splits[2].trim());
				Date date = DateUtils.parseDate(dateString, new String[] { "yyyy-MM-dd HH:mm:ss" });
				add(word, date);
				if (isFavorite && !isFavorite(word)) {
					setFavorite(word, true);
				}
			}
		} catch (IOException e) {
			logger.error("load history file fail. file : " + historyFile.getAbsolutePath(), e);
		} catch (ParseException e) {
			logger.error("can't parser date", e);
		}

	}

	public void markFavorite(String word) {
		setFavorite(word, true);
	}

	public void save() {
		try {
			FileUtils.writeLines(historyFile, histories);
		} catch (IOException e) {
			logger.error("save history file fail. file : " + historyFile.getAbsolutePath(), e);
		}

	}

	public void setHistoryFile(File historyFile) {
		this.historyFile = historyFile;
	}

	@Override
	public Map<String, Integer> statistic() {
		Map<String, Integer> results = new TreeMap<String, Integer>();
		for (History history : histories) {
			String word = history.getWord();
			if (results.containsKey(word)) {
				Integer count = results.get(word);
				count += 1;
				results.put(word, count);
			} else {
				results.put(word, 1);
			}
			logger.debug("the count of word '{}' is {}", word, results.get(word));
		}
		return results;
	}

	public void unmarkFavorite(String word) {
		setFavorite(word, false);
	}

	private void setFavorite(String word, boolean favorite) {
		for (History each : histories) {
			if (StringUtils.equalsIgnoreCase(word, each.getWord())) {
				each.setFavorite(favorite);
			}
		}
	}

	/**
	 * NOTE: this method only used for testing pupose, don't use it in production code. 
	 * @param word the word.
	 * @param date the added date.
	 */
	protected void add(String word, Date date) {
		History history = new History(date, word.toLowerCase());
		logger.debug("add to history [{}] ", history);
		histories.add(history);
	}

}
